package chatControler;

import chatGUI.DemandeFichier;

import java.io.File;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;

import messages.*;
import chatModel.*;


/**
 * Classe Controler : c'est le coeur du ChatSystem : il décide des actions à prendre en fonction des éveènements
 * 
 * 
 */
public class ChatControler {

	private boolean connected;
	private Utilisateur localUser;
	private ArrayList<UtilisateurDistant> otherUserList = new ArrayList<UtilisateurDistant>();
	/**
	 * Constructeur
         * Initialise un utilisateur avec des attribus null
         * Initialise la liste des utilisateurs distants
	 */
	public ChatControler() {
		localUser = new Utilisateur(null, null);
		otherUserList = new ArrayList();
	}
        
        /**
         * Permet d'obtenir l'utilisateur local
         * @return L'utilisateur local
         */
	public Utilisateur getLocalUser() {
		return localUser;
	}

        /**
         * Permet d'associer l'utilisateur local au ChatControler
         * @param localUser L'utilisateur local
         */
	public void setLocalUser(Utilisateur localUser) {
		this.localUser = localUser;
	}
        
        /**
         * Donne la liste des utilisateurs distants
         * @return Liste des utilisateurs distants
         */
	public ArrayList getOtherUserList(){
		return this.otherUserList;
	}
        
        /**
         * Permet de savoir dans quel état est le chatControler
         * @return true si connecté au réseau, false sinon
         */
	public boolean isConnected() {
		return connected;
	}
        
        /**
         * Permet de changer d'état
         * En faisant la connection, le GUI change de Fenetre et le NI envoie le hello en broadcast
         * @param connected Booléen indiquant l'état de connexion
         */
	public void setConnected(boolean connected) {
		if (connected==true){
			System.out.println("connect controler");
			this.connected = connected;
			this.localUser.getListeUser().addElement(this.otherUserList.get(0).getUserName());
			this.localUser.connect();
		}
		if(connected==false){
			this.connected=connected;
			this.localUser.disconnect();
			this.otherUserList.clear();
		}
	}

	
	/**
         * Indique si le fichié a été envoyé à un utilisateur distant
         * @see chatModel.Utilisateur#fichierEnvoyeOuRecu(chatModel.UtilisateurDistant, java.lang.String) fichierEnvoyeOuRecu
         * @param UtilisteurDistant
         */
	public void fichierEnvoye(UtilisateurDistant user){
		String msg = "*** Votre fichier a bien été envoyé à " + user.getUserName() + " ! ***";
		this.localUser.fichierEnvoyeOuRecu(user, msg);
	}
	
        /**
         * Indique si le fichié a été reçu depuis un utilisateur distant
         * @see chatModel.Utilisateur#fichierEnvoyeOuRecu(chatModel.UtilisateurDistant, java.lang.String) fichierEnvoyeOuRecu
         * @param UtilisateurDistant
         */
	public void fichierRecu(UtilisateurDistant user){
		String msg = "*** Le fichier de " + user.getUserName() + " a bien été reçu ! *** ";
		this.localUser.fichierEnvoyeOuRecu(user, msg);
	}
	
        /**
         * Envoi un message à un utilisateur distant
         * @see chatModel.Utilisateur#sendMsgToNIandGUI(chatModel.UtilisateurDistant, java.lang.String) sendMsgToNIandGUI
         * @param pseudoUserDistant Pseudo de l'utilisateur distant
         * @param msgToSend Message à envoyer
         */
	public void envoyerMsg(String pseudoUserDistant, String msgToSend){
		int i =0;
		boolean trouve = false;
		UtilisateurDistant user=null;
		System.out.println("je rentre dans la méthode du controler");
		while( (i< otherUserList.size()) && (!trouve) ){
			System.out.println("je cherche le type");
			//si l'utilisateur existait on le trouve
			if (otherUserList.get(i).getUserName().compareTo(pseudoUserDistant)==0){
				user = otherUserList.get(i);
				trouve=true;
				this.localUser.sendMsgToNIandGUI(user, msgToSend);
			}
			i++;
		}
		System.out.println("j'envoie au model");
	}
        
        /**
         * Envoyer une demande de fichier à un utilisateur
         * @see chatModel.Utilisateur#sendDemandeFileToNI(chatModel.UtilisateurDistant, java.io.File) sendDemandeFileToNI 
         * @param pseudoUserDistant Pseudo de l'utilisateur distant
         * @param file Fichier local à proposer
         */
	public void envoyerDemandeFile(String pseudoUserDistant, File file){
		int i =0;
		boolean trouve = false;
		UtilisateurDistant user=null;
		System.out.println("je rentre dans la méthode du controler");
		while( (i< otherUserList.size()) && (!trouve) ){
			System.out.println("je cherche le type");
			//si l'utilisateur existait on le trouve
			if (otherUserList.get(i).getUserName().compareTo(pseudoUserDistant)==0){
				user = otherUserList.get(i);
				trouve=true;
				this.localUser.sendDemandeFileToNI(user, file);
			}
			i++;
		}
		System.out.println("j'envoie au model");
	}

        /**
         * Accepter un fichier proposé par un utilisateur distant
         * @see chatModel.Utilisateur#acceptFileToNI(chatModel.UtilisateurDistant, java.io.File) acceptFileToNI 
         * @param UserDistant Utilisateur distant qui a proposé le fichier
         * @param nameFile Nom du fichier qui a été proposé
         * @param file Futur fichier reçu
         */
	public void acceptFile(UtilisateurDistant UserDistant, String nameFile,File file){
		File fileAccepted = new File(file.getPath()+"/"+nameFile);
		System.out.println("chemin fichier à creer : " + fileAccepted.getPath());
		this.localUser.acceptFileToNI(UserDistant, fileAccepted);
	}
        
        /**
         * Refuser un fichier proposé par un utilisateur distant
         * @see chatModel.Utilisateur#refuseFileToNI(chatModel.UtilisateurDistant, java.lang.String) refuseFileToNI
         * @param UserDistant Utilisateur distant ayant proposé le fichier
         * @param nomFichier Nom du fichier proposé
         */
	public void refuseFile(UtilisateurDistant UserDistant, String nomFichier){
		this.localUser.refuseFileToNI(UserDistant, nomFichier);
	}

        /**
         * Quitter le ChatSystem
         * @see chatModel.Utilisateur#close() 
         */
	public void close(){
		System.out.println("controler quitte");
		this.localUser.close();
	}

        /**
         * Traiter les messages reçus par le chatNI
         * @param recvMessage Message reçu sur le socket UDP
         * @see chatModel.Utilisateur#notifyChatNISendNegHello(chatModel.UtilisateurDistant) notifyChatNISendNegHello
         * @see chatModel.Utilisateur#notifyGUINewMsg(chatModel.UtilisateurDistant, java.lang.String) notifyGUINewMsg
         * @see chatModel.Utilisateur#notifyGUIFileRequest(chatModel.UtilisateurDistant, java.lang.String) notifyGUIFileRequest
         * @see chatModel.Utilisateur#notifyNISendFile(chatModel.UtilisateurDistant, java.lang.String) notifyNISendFile
         * @throws UnknownHostException Impossible de trouver l'IP de l'utilisateur distant
         * @throws MessageCreationException Impossible de créer un message
         */
	public void traiterNewMsg(Message recvMessage /*,ChatNI interfaceReseau*/) throws UnknownHostException, MessageCreationException {
		boolean request;
		boolean accepted;
		UtilisateurDistant distantUser, tempDistantUser;

		// Ici, on regarde quel est le type de message qui arrive, et on fait une action en fonction !
		if((localUser.getUserName().equals(recvMessage.getUsername()))){
			localUser.setMyIp(InetAddress.getByName(recvMessage.getHostName()));
			System.out.println("j'ai reçu un message de moi, je suis connecté : " + localUser.getUserName());
		}
		//on vérifie que ce n'est pas nous qui envoyons le message, sinon probleme quand on broadcast
		if ((recvMessage.getHostName().compareTo(localUser.getMyIp().getHostName()) != 0)
				|| ((recvMessage.getUsername().compareTo(localUser.getUserName())))!=0) {
			System.out.println("Traitement d'un message reçu");
			//1er cas : un reçoit un Hello d'un autre utilisateur
			//On verifie également que l'utilisateur n'est pas déjà
			tempDistantUser = new UtilisateurDistant(recvMessage.getUsername()+"@"+recvMessage.getHostName(), InetAddress.getByName(recvMessage.getHostName()));
			if (recvMessage instanceof Hello && !(this.otherUserList.contains(tempDistantUser))) {
				System.out.print("On a reçu un Hello ");
				//Ajout d'un nouvel utilisateur distant
				//distantUser=new UtilisateurDistant(recvMessage.getUsername(), InetAddress.getByName(recvMessage.getHostName()));
				distantUser = tempDistantUser;
				otherUserList.add(distantUser);
				this.localUser.getListeUser().addElement(distantUser.getUserName());
				System.out.println("j'ai reçu un hello de : " + tempDistantUser.getUserName());
				//Ici mettre l'update de la liste dans le ChatGUI
				//On reçoit un Hello positif = Nouvel utilisateur sur le réseau
				if (((Hello) recvMessage).getWaitAck()) {
					//On envoie un hello négatif a cet utilisateur
					System.out.println("positif");
					//On dit au NI d'envoyer l'ack false, et on met à jour la liste d'utilisateur dans le GUI
					this.localUser.notifyChatNISendNegHello(distantUser);
					String msg = "     ***" + distantUser.getUserName() + " s'est connecté ! ***";
					this.localUser.afficheConnectDeconnect(distantUser, msg);
				}
			}  
			else {//Cas ou l'utilisateur est déjà connu
				/*Si on reçoit un nouveau message, on verifie si l'utilisateur existe*/
				if (recvMessage instanceof ChatMessage) {
					distantUser = tempDistantUser;
					System.out.println("msg reçu de " + distantUser.getUserName());
					this.localUser.notifyGUINewMsg(distantUser, ((ChatMessage) recvMessage).getMsgToSend());


				} else if (recvMessage instanceof FileNotif) {
					/*-------------------------------------
					 * On reçoit une demande de fichier----
	                    -------------------------------------*/
					System.out.println("instance filenotif");
					if (((FileNotif) recvMessage).getRequest() == true) {
						distantUser = tempDistantUser;
						System.out.println("on doit accepter/refuser un fichier");
						this.localUser.notifyGUIFileRequest(distantUser, ((FileNotif) recvMessage).getNameFile());
					}
					/*-------------------------------------
					 * On reçoit un acceptation de fichier-
	                 ---------------------------------------*/
					else if ((((FileNotif) recvMessage).getRequest() == false) && ((((FileNotif) recvMessage).getAccepted() == true))) {
						distantUser = tempDistantUser;	
						System.out.println("il accepte le fichier");
						this.localUser.notifyNISendFile(distantUser, ((FileNotif) recvMessage).getNameFile());
					}
					else if ((((FileNotif) recvMessage).getRequest() == false) && ((((FileNotif) recvMessage).getAccepted() == false))) {
						distantUser = tempDistantUser;
						System.out.println("il refuse le fichier");
						this.localUser.notifyGUIFileRefused(distantUser, ((FileNotif) recvMessage).getNameFile());
					}
					
				} else if (recvMessage instanceof Bye) {
					distantUser = tempDistantUser;
					boolean trouve = false;
					int i = 0;
					while(i<this.otherUserList.size()&& !trouve){
						if(this.otherUserList.get(i).equals(distantUser)){
							trouve=true;
							this.otherUserList.remove(i);
							this.localUser.getListeUser().remove(i);
						}
						i++;
					}
					String msg = "     ***" + distantUser.getUserName() + " s'est deconnecté ! ***";
					this.localUser.afficheConnectDeconnect(distantUser, msg);
				}
			}
		}
	}

}